#include "QQHandle.h"

QQHandle* QQHandle::m_instance = nullptr;

QQHandle::QQHandle(QObject* parent)
	: QObject(parent)
{
}

QQHandle::~QQHandle()
{
}

QQHandle* QQHandle::get_instance()
{
	if (m_instance == nullptr)
		m_instance = new QQHandle;
	return m_instance;
}

void QQHandle::add_new_socket(QTcpSocket* socket)
{
	socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
	m_sockets.insert(socket, "");
	m_accounts.insert("", socket);
	connect(socket, &QTcpSocket::readyRead, this, &QQHandle::do_ready_read);
	connect(socket, &QTcpSocket::errorOccurred, this, &QQHandle::do_tcp_error);
	connect(socket, &QTcpSocket::disconnected, this, &QQHandle::do_tcp_disconnected);
	qInfo() << "new connection";
}

QString QQHandle::generate_token(QVariantList& list)
{
	QByteArray temp;
	for (int i = 0; i < 32; ++i)
		temp.append(QRandomGenerator::global()->generate() % 256);
	QString secret = QString::fromUtf8(QCryptographicHash::hash(temp, QCryptographicHash::Sha256).toHex());
	auto token = jwt::create()
		.set_type("JWT")
		.set_payload_claim("account", jwt::claim(list[0].toString().toStdString()))
		.set_payload_claim("device", jwt::claim(list[1].toString().toStdString()))
		.sign(jwt::algorithm::hs256{ secret.toStdString() });
	return QString::fromStdString(token);
}

void QQHandle::do_ready_read()
{
	QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());
	QByteArray data = socket->readAll();
	QJsonObject request = QJsonDocument::fromJson(data).object();
	if (request["type"] == "login")
	{
		handle_login(request, socket);
	}
	else if (request["type"] == "register")
	{
		handle_register(request, socket);
	}
	else if (request["type"] == "find_password")
	{
		QJsonObject response;
		response["type"] = "find_password";
		response["status"] = "success";
		socket->write(QJsonDocument(response).toJson());
	}
	else if (request["type"] == "add_friend")
	{
		handle_add_friend(request, socket);
	}
	else if (request["type"] == "delete_friend")
	{
		qq_sql->delete_friend(m_sockets[socket], request["friend"].toString());
		QJsonObject response;
		response["type"] = "delete_friend";
		response["friend"] = request["friend"];
		socket->write(QJsonDocument(response).toJson());
	}
	else if (request["type"] == "send_message")
	{
		handle_send_message(request, socket);
	}
}

void QQHandle::handle_login(QJsonObject& request, QTcpSocket* socket)
{
	QJsonObject response;
	response["type"] = "login";
	QString account = request["account"].toString();
	QString password = request["password"].toString();
	QString device = request["device"].toString();

	bool existed = qq_sql->check_user_exist(account, password);
	if (existed)
	{
		bool logined = !qq_sql->check_token_null(account);
		QVariantList list;
		list << account << password << device;
		QString token = generate_token(list);
		bool new_device = !qq_sql->check_token(account, token);
		if (!logined)
		{
			response["status"] = "success";
			response["token"] = token;
			m_sockets[socket] = account;
			m_accounts[account] = socket;
			qq_sql->update_token(account, token);
			qDebug() << account + " connected";
		}
		else if (logined && new_device)
		{
			QTcpSocket* old_socket = m_accounts[account];
			QJsonObject message;
			message["type"] = "out_line";
			message["reason"] = "other_login";
			old_socket->write(QJsonDocument(message).toJson());
			old_socket->disconnect();

			m_sockets[socket] = account;
			m_accounts[account] = socket;
			qq_sql->update_token(account, token);
			qInfo() << account + " new device connected";

			response["status"] = "success";
			response["token"] = token;
		}
	}
	else
	{
		response["status"] = "fail";
	}
	socket->write(QJsonDocument(response).toJson());
}

void QQHandle::handle_register(QJsonObject& request, QTcpSocket* socket)
{
	QJsonObject response;
	response["type"] = "register";
	QString account = qq_sql->register_new_user(request["password"].toString(), request["nickname"].toString());
	response["account"] = account;
	socket->write(QJsonDocument(response).toJson());
}

void QQHandle::do_tcp_error(QAbstractSocket::SocketError error)
{
	QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());
	switch (error)
	{
	case QAbstractSocket::ConnectionRefusedError:
		break;
	case QAbstractSocket::RemoteHostClosedError:
		break;
	case QAbstractSocket::HostNotFoundError:
		break;
	case QAbstractSocket::SocketAccessError:
		break;
	case QAbstractSocket::SocketResourceError:
		break;
	case QAbstractSocket::SocketTimeoutError:
		break;
	case QAbstractSocket::DatagramTooLargeError:
		break;
	case QAbstractSocket::NetworkError:
		break;
	case QAbstractSocket::AddressInUseError:
		break;
	case QAbstractSocket::SocketAddressNotAvailableError:
		break;
	case QAbstractSocket::UnsupportedSocketOperationError:
		break;
	case QAbstractSocket::UnfinishedSocketOperationError:
		break;
	case QAbstractSocket::ProxyAuthenticationRequiredError:
		break;
	case QAbstractSocket::SslHandshakeFailedError:
		break;
	case QAbstractSocket::ProxyConnectionRefusedError:
		break;
	case QAbstractSocket::ProxyConnectionClosedError:
		break;
	case QAbstractSocket::ProxyConnectionTimeoutError:
		break;
	case QAbstractSocket::ProxyNotFoundError:
		break;
	case QAbstractSocket::ProxyProtocolError:
		break;
	case QAbstractSocket::OperationError:
		break;
	case QAbstractSocket::SslInternalError:
		break;
	case QAbstractSocket::SslInvalidUserDataError:
		break;
	case QAbstractSocket::TemporaryError:
		break;
	case QAbstractSocket::UnknownSocketError:
		break;
	default:
		break;
	}
}

void QQHandle::do_tcp_disconnected()
{
	QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());
	QString account = m_sockets[socket];
	if (!account.isEmpty())
	{
		m_accounts.remove(account);
		m_sockets.remove(socket);
		qq_sql->update_token(account, QString());
		qInfo() << account + " disconnected";
	}
	else
	{
		qInfo() << "disconnected";
	}
}

void QQHandle::handle_add_friend(QJsonObject& request, QTcpSocket* socket)
{
	QString account = request["account"].toString();
	QString friend_account = request["friend"].toString();
	if (account == friend_account || qq_sql->test_is_friend(account, friend_account))
	{
		QJsonObject response;
		response["type"] = "add_friend";
		response["account"] = request["account"];
		response["friend"] = request["friend"];
		response["status"] = "error";
		socket->write(QJsonDocument(response).toJson());
	}
	else if (request["status"] == "request")
	{
		QJsonObject response;
		response["type"] = "add_friend";
		response["account"] = request["friend"];
		response["friend"] = request["account"];
		response["status"] = "request";
		QTcpSocket* friend_socket = m_accounts[request["friend"].toString()];
		friend_socket->write(QJsonDocument(response).toJson());
	}
	else if (request["status"] == "agree")
	{
		qq_sql->add_friend(request["account"].toString(), request["friend"].toString());

		QJsonObject response;
		response["type"] = "add_friend";
		response["status"] = "agree";
		response["account"] = request["account"];

		QQUser _friend = qq_sql->get_user(request["friend"].toString());
		QJsonObject object;
		object["account"] = _friend.m_account;
		object["nickname"] = _friend.m_nickname;
		QString icon_path = _friend.m_icon;
		QFile file(icon_path);
		file.open(QIODevice::ReadOnly);
		object["icon"] = QString(file.readAll().toBase64());
		file.close();
		response["friend"] = QString(QJsonDocument(object).toJson(QJsonDocument::Compact));
		socket->write(QJsonDocument(response).toJson());

		_friend = qq_sql->get_user(request["account"].toString());
		response["account"] = request["friend"];
		object["account"] = _friend.m_account;
		object["nickname"] = _friend.m_nickname;
		icon_path = _friend.m_icon;
		file.setFileName(icon_path);
		file.open(QIODevice::ReadOnly);
		object["icon"] = QString(file.readAll().toBase64());
		response["friend"] = QString(QJsonDocument(object).toJson(QJsonDocument::Compact));
		QTcpSocket* friend_socket = m_accounts[request["friend"].toString()];
		friend_socket->write(QJsonDocument(response).toJson());
	}
	else if (request["status"] == "refuse")
	{
		QJsonObject response;
		response["type"] = "add_friend";
		response["account"] = request["friend"];
		response["friend"] = request["account"];
		response["status"] = "refuse";
		socket->write(QJsonDocument(response).toJson());
	}
}

void QQHandle::handle_send_message(QJsonObject& request, QTcpSocket* socket)
{
	QString account = request["account"].toString();
	QString friend_account = request["receiver"].toString();
	if (account == friend_account || !qq_sql->test_is_friend(account, friend_account))
	{
		QJsonObject response;
		response["type"] = "send_message";
		response["account"] = request["account"];
		response["friend"] = request["friend"];
		response["status"] = "error";
		socket->write(QJsonDocument(response).toJson());
	}
	else
	{
		QTcpSocket* friend_socket = m_accounts[friend_account];
		QJsonObject response;
		response["type"] = "send_message";
		response["sender"] = account;
		response["receiver"] = friend_account;
		response["message"] = request["message"];
		friend_socket->write(QJsonDocument(response).toJson());
	}
}
